﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using mal.Web.Silverlight.MEF.Core.Interfaces;
using System.ComponentModel.Composition;
using mal.Web.Silverlight.MEF.Core.Deployment;
using System.Linq;
using System.Collections.Generic;
using mal.Web.Silverlight.MEF.Core.Navigation;

namespace mal.Web.Silverlight.MEF.Core.ContentLoader
{
    /// <summary>
    /// Loader that uses MEF to locate Pages for the given Uris.
    /// </summary>
    public class MEFLoader : LoaderBase
    {
        /// <summary>
        /// ContentLoader that uses MEF to locate Pages for the given Uris.
        /// </summary>
        /// <seealso cref="MEFContentLoader"/>
        /// <seealso cref="LoaderBase"/>
        private MEFContentLoader parent;

        /// <summary>
        /// Module which contain the requested page
        /// </summary>
        private Module targetModule;

        private Dictionary<string, string> queryString;

        public MEFLoader(MEFContentLoader parent)
        {
            this.parent = parent;
            ModulesLoader.Instance.ModulesAvailable += new ModulesAvailableHandler(Instance_ModulesAvailable);
        }
        
        /// <summary>
        /// Use MEF to load module that contains the targetUri         
        /// </summary>
        /// <param name="targetUri">The Uri being loaded.</param>
        /// <param name="currentUri">The current Uri.</param>
        public override void Load(Uri targetUri, Uri currentUri)
        {
            try
            {
                bool moduleLoaded = false;
                this.targetModule = GetModuleFromUri(targetUri);
                this.queryString = GetQueryString(targetUri);

                if (!parent.Initialized)
                    parent.Initialize();

                //Some modules have been loaded
                if (parent.PageFactories != null && parent.PageFactories.Count() > 0)
                {
                    //The requested page is in a module already loaded
                    var pageFactory = parent.PageFactories.SingleOrDefault((factory) => CompareUri(factory, GetTrimmedUri(this.targetModule.Uri)));
                    if (pageFactory != null)
                    {
                        moduleLoaded = true;
                        var pageContext = pageFactory.CreateExport();                        
                        Complete(pageContext.Value);
                    }                    
                }
                
                //The page is in module not yet loaded
                if(!moduleLoaded)
                {
                    //Load the module using MEF
                    ModulesLoader.Instance.LoadModule(this.targetModule);
                }
            }
            catch (Exception e)
            {
                Error(e);
            }
        }

        protected void Complete(Page page)
        {
            NavigationContext.Instance.QueryString = this.queryString;

            base.Complete(page);
        }

        /// <summary>
        /// Called when the module's imports have been satisfied and it is safe to use.
        /// </summary>
        /// <param name="modules"></param>
        private void Instance_ModulesAvailable(Lazy<Page, IModuleMetadata>[] modules)
        {
            ModulesLoader.Instance.ModulesAvailable -= new ModulesAvailableHandler(Instance_ModulesAvailable);
     

            //Search the requested page in the module loaded
            foreach (var module in modules)
            {
                if (GetTrimmedUri(module.Metadata.NavigateUri) == GetTrimmedUri(targetModule.Uri))
                {
                    Complete(module.Value);
                }
            }
        }

        /// <summary>
        /// Get the xap package from the target uri
        /// </summary>
        /// <param name="targetUri"></param>
        /// <remarks>The target uri must be like : XapUri;PageNavigationUri</remarks>
        /// <returns>The requested module</returns>
        private Module GetModuleFromUri(Uri targetUri)
        {
            Module module = new Module();

            string uriString = targetUri.ToString();

            int uriStartIndex;
            int uriLength;

            uriStartIndex = uriString.IndexOf(';');

            if (uriString.Contains('?'))
                uriLength = uriString.IndexOf('?') - uriStartIndex-1;
            else
                uriLength = uriString.Length - uriStartIndex-1;

            module.Package = uriString.Substring(0, uriStartIndex);
            module.Uri = uriString.Substring(uriStartIndex + 1, uriLength);

            return module;
        }   

        private bool CompareUri(ExportFactory<Page, IModuleMetadata> factory, Uri targetUri)
        {
            Uri uri = GetTrimmedUri(factory.Metadata.NavigateUri);
            return targetUri.Equals(uri);
        }

        private static Uri GetTrimmedUri(string uriString)
        {
            if (uriString.Contains('?'))
                uriString = uriString.Substring(0, uriString.IndexOf('?') + 1);
            Uri result;
            try
            {
                result = new Uri(uriString, UriKind.Absolute);
            }
            catch
            {
                result = new Uri(uriString, UriKind.Relative);
            }
            return result;
        }

        /// <summary>
        /// Extract the querystring key/value dictionary from the targetUri
        /// </summary>
        /// <param name="targetUri"></param>
        /// <returns></returns>
        private Dictionary<string, string> GetQueryString(Uri targetUri)
        {
            Dictionary<string, string> queryStringLocal = null;
            string uriString = targetUri.ToString();
            if (uriString.Contains('?'))
            {
                queryStringLocal = new Dictionary<string, string>();

                int startIndex = uriString.IndexOf('?')+1;
                uriString = uriString.Substring(startIndex, uriString.Length - startIndex);

                string[] queryStringArray = uriString.Split("&".ToCharArray());
                foreach (string item in queryStringArray)
                {
                    string[] itemArray = item.Split("=".ToCharArray());
                    if (itemArray.Length == 2)
                    {
                        queryStringLocal.Add(itemArray[0], itemArray[1]);
                    }
                }                
            }

            return queryStringLocal;
        }

        public override void Cancel()
        {
        }
    }
}
